#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>
#include <glew.h>
#include <glut.h>
#include <Cg/cgGL.h>

#pragma comment( lib,"glew32.lib" )
#pragma comment( lib,"cg.lib" )
#pragma comment( lib,"cgGL.lib" )

GLuint readID,writeID;
GLuint fb;
GLuint glutWindowHandle;

CGcontext cgContext;
CGprofile fragmentProfile;
CGprogram fragmentProgram;
CGparameter cgData;

int width=300, height=300;

float* data;
float* result;


void display()
{


	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	/************************************************************************/
	/* Configure Culling Space                                              */
	/************************************************************************/

	


	/************************************************************************/
	/* Initialize Essential Buffers or Environment                          */
	/************************************************************************/

	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);

	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, writeID, 0);
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_RECTANGLE_ARB, readID, 0);

	//glBindTexture(GL_TEXTURE_RECTANGLE_ARB,readID);
	cgGLEnableProfile(fragmentProfile);
	cgGLBindProgram(fragmentProgram);

	//glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT);

	cgGLSetTextureParameter(cgData, readID);
	cgGLEnableTextureParameter(cgData);

	/************************************************************************/
	/* Trigger GPU Computation                                              */
	/************************************************************************/

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0.0, width, 0.0, height);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glViewport(0, 0, width, height);

	glPolygonMode(GL_FRONT,GL_FILL);

	glBegin(GL_QUADS);
	glTexCoord2f(0.0, 0.0); 
	glVertex2f(0.0, 0.0);
	glTexCoord2f(width, 0.0); 
	glVertex2f(width, 0.0);
	glTexCoord2f(width, height); 
	glVertex2f(width, height);
	glTexCoord2f(0.0, height); 
	glVertex2f(0.0, height);
	glEnd();

	/************************************************************************/
	/* Read Buffers to Get the Feedback                                     */
	/************************************************************************/

	cgGLDisableProfile(fragmentProfile);

	

	//glReadPixels(0, 0, width, height,GL_RGB,GL_FLOAT,result);

	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,0);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0.0, width*2, 0.0, height*2);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glViewport(0, 0, width*2, height*2);

	glPolygonMode(GL_FRONT,GL_FILL);

	glEnable(GL_TEXTURE_RECTANGLE_ARB);
	glBindTexture(GL_TEXTURE_RECTANGLE_ARB,readID);

	glBegin(GL_QUADS);
	glTexCoord2f(0.0, 0.0); 
	glVertex2f(0.0, 0.0);
	glTexCoord2f(width, 0.0); 
	glVertex2f(width*2, 0.0);
	glTexCoord2f(width, height); 
	glVertex2f(width*2, height*2);
	glTexCoord2f(0.0, height); 
	glVertex2f(0.0, height*2);
	glEnd();

	glDisable(GL_TEXTURE_RECTANGLE_ARB);

	
	

	//glDrawPixels(width,height,GL_RGB,GL_FLOAT,result);

	glutSwapBuffers();

	GLuint temp=readID;

	readID=writeID;

	writeID=temp;

	//printf("hello");

	

	glutPostRedisplay();

}



int main(int argc, char **argv)
{
	int index;


	data=(float*)malloc(width*height*3*sizeof(float));
	result=(float*)malloc(width*height*3*sizeof(float));

	for (int i=0;i<width;i++)
	{
		for (int j=0;j<height;j++)
		{
			float flip;

			if (i<width/2)
			{
				flip=0.5;
			}else{
				flip=1.0;
			}
			data[3*(i*height+j)]=data[3*(i*height+j)+1]=data[3*(i*height+j)+2]=flip;
		}
	}


	

	/************************************************************************/
	/* Start                                                                */
	/************************************************************************/


	/************************************************************************/
	/* Initialize Operation                                                 */
	/************************************************************************/

	glutInit ( &argc, argv );
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
	glutInitWindowSize(width*2, height*2);
	glutWindowHandle = glutCreateWindow("GPGPU");
	glutDisplayFunc(display);
	glewInit();

	/************************************************************************/
	/* Create and Bind FBO                                                  */
	/************************************************************************/

	glGenFramebuffersEXT(1, &fb); 
	


	/************************************************************************/
	/* Create and Configure Textures                                        */
	/************************************************************************/

	glGenTextures(1,&readID);
	glGenTextures(1,&writeID);


	glBindTexture(GL_TEXTURE_RECTANGLE_ARB,readID);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP);
	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,0,GL_RGB32F_ARB,width,height,0,GL_RGB,GL_FLOAT,data);

	//glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB,0,0,0,width,height,GL_RGB,GL_FLOAT,data);			//Send Data to the Visual Memory





	glBindTexture(GL_TEXTURE_RECTANGLE_ARB,writeID);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP);
	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,0,GL_RGB32F_ARB,width,height,0,GL_RGB,GL_FLOAT,result);



	//glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

	/************************************************************************/
	/* Initialize Nvidia Cg Environment                                     */
	/************************************************************************/

	cgContext = cgCreateContext();
	fragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
	cgGLSetOptimalOptions(fragmentProfile);
	fragmentProgram = cgCreateProgramFromFile (cgContext, CG_SOURCE, "GP_F.cg", fragmentProfile, NULL, NULL);
	cgGLLoadProgram (fragmentProgram);
	cgData = cgGetNamedParameter (fragmentProgram, "data");

	



	glutMainLoop();

	/************************************************************************/
	/* Release Resource                                                    */
	/************************************************************************/

	cgDestroyProgram(fragmentProgram);
	cgDestroyContext(cgContext);
	glDeleteFramebuffersEXT(1,&fb);
	glDeleteTextures(1,&readID);
	glDeleteTextures(1,&writeID);
	glutDestroyWindow (glutWindowHandle);


	/************************************************************************/
	/* End                                                                  */
	/************************************************************************/


	delete [] result;
	delete [] data;

	return 0;
}